﻿using System;
using System.Collections.Generic;
using System.Text;
using NLog;
using NGinnBPM.Lib.Schema;
using NGinnBPM.Runtime;
using NGinnBPM.Runtime.Tasks;
using NGinnBPM.Lib.Exceptions;
using NGinnBPM.MessageBus;

namespace NGinnBPM.Services
{
    /// <summary>
    /// Task instance factory
    /// </summary>
    public class TaskInstanceFactory : IDefaultTaskInstanceFactory
    {
        public IServiceResolver ServiceLocator { get; set; }
        
        #region ITaskInstanceFactory Members

        public NGinnBPM.Runtime.TaskInstance CreateTaskInstance(NGinnBPM.Lib.Schema.Task tsk)
        {
            if (tsk is CompositeTask)
            {
                CompositeTaskInstance cti = new CompositeTaskInstance();
                cti.ProcessDefinitionId = tsk.ParentProcessDefinition.DefinitionId;
                cti.TaskId = tsk.Id;
                return cti;
            }
            else if (tsk is AtomicTask)
            {
                TaskInstance ti;
                AtomicTask at = tsk as AtomicTask;
                if (at.TaskType == NGinnTaskType.Empty)
                {
                    ti = new EmptyTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.Timer)
                {
                    ti = new TimerTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.Subprocess)
                {
                    ti = new SubprocessTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.SendMessage)
                {
                    ti = new SendMessageTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.ReceiveMessage)
                {
                    ti = new ReceiveMessageTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.RaiseError)
                {
                    ti = new RaiseErrorTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.Manual)
                {
                    ti = new ManualTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.Notification)
                {
                    ti = new NotificationTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.XmlHttp)
                {
                    ti = new HttpRequestTaskInstance();
                }
                else if (at.TaskType == NGinnTaskType.Custom)
                {
                    if (at.CustomType == null || at.CustomType.Length == 0)
                        throw new NGinnException("Custom type not defined for task " + at.Id).SetProcessDef(at.ParentProcessDefinition.DefinitionId);
                    if (ServiceLocator == null) throw new NGinnException("Service locator not set");
                    int idx = at.CustomType.IndexOf('.');
                    if (idx < 0) throw new NGinnException("Custom type must contain package name [package.taskType]").SetProcessDef(at.ParentProcessDefinition.DefinitionId);
                    string pkg = at.CustomType.Substring(0, idx);
                    ITaskInstanceFactory custFact = ServiceLocator.GetInstance<ITaskInstanceFactory>("TaskInstanceFactory_" + pkg);
                    if (custFact == null) 
                        throw new NGinnException("Custom task instance factory not found: TaskInstanceFactory_" + pkg)
                            .SetProcessDef(at.ParentProcessDefinition.DefinitionId).SetTaskId(at.Id);
                    return custFact.CreateTaskInstance(at);
                }
                else throw new Exception("Unknown task type: " + at.TaskType);
                ti.ProcessDefinitionId = tsk.ParentProcessDefinition.DefinitionId;
                ti.TaskId = tsk.Id;
                return ti;
            }
            else throw new Exception();
        }

        #endregion
    }
}
